package de.tempel.fittslaw.objects;

import java.awt.geom.Point2D;
import java.text.DecimalFormat;
import java.util.List;

import processing.core.PApplet;
import de.tempel.fittslaw.objects.interfaces.Drawable;
import de.tempel.fittslaw.regression.Line;

public class CoordinateSystem implements Drawable {

	private static int DISTANCE_TO_BORDERS = 30;
	private static int DISTANCE_OF_STROKES_TO_BORDERS = 20;

	private Point2D position; // linke obere ecke des ks im window

	private int width; // breite in window
	private int height; // höhe in window

	private float minX;
	private float minY;
	private float maxX;
	private float maxY;

	private List<Line> lines;
	private List<List<Point2D>> pointsForLine;
	private PApplet window;

	private float stepX;
	private float stepY;

	private float labelStepX;
	private float labelStepY;

	public CoordinateSystem(Point2D position, int width, int height,
			float minX, float minY, float maxX, float maxY, List<Line> lines,
			List<List<Point2D>> pointsForLine) {

		this.position = position;
		this.width = width;
		this.height = height;
		this.minX = minX;
		this.minY = minY;
		this.maxX = maxX;
		this.maxY = maxY;
		this.lines = lines;
		this.pointsForLine = pointsForLine;
	}

	@Override
	public void draw(PApplet window) {

		this.window = window;
		drawCoordinateSystem();

		int i = 0;
		int j = 0;
		for (Line line : lines) {
			drawLine(line, color(j));
			j++;
			drawPoints(pointsForLine.get(i), color(j));
			i++;
			j++;

		}

	}

	private Integer[] color(int i) {

		Integer[] retColor = new Integer[3];
		switch (i % 6) {
		// grün
		case (0):
			retColor[0] = 0;
			retColor[1] = 194;
			retColor[2] = 6;
			break;
		case (1):
			retColor[0] = 0;
			retColor[1] = 153;
			retColor[2] = 51;
			break;
		// pink
		case (2):
			retColor[0] = 204;
			retColor[1] = 51;
			retColor[2] = 255;
			break;
		case (3):
			retColor[0] = 245;
			retColor[1] = 0;
			retColor[2] = 184;
			break;
		// blau
		case (4):
			retColor[0] = 51;
			retColor[1] = 204;
			retColor[2] = 255;
			break;
		case (5):
			retColor[0] = 51;
			retColor[1] = 102;
			retColor[2] = 255;
			break;
		}
		return retColor;
	}

	private void drawCoordinateSystem() {

		window.stroke(0);
		window.fill(255);

		window.rect((float) position.getX(), (float) position.getY(), width,
				height);

		window.fill(0);

		stepX = (width - 2 * DISTANCE_TO_BORDERS) / (maxX - minX);
		stepY = (height - 2 * DISTANCE_TO_BORDERS) / (maxY - minY);

		labelStepX = maxX / 10f;
		labelStepY = maxY / 10f;

		// y-Achse
		window.line((float) position.getX() + DISTANCE_TO_BORDERS,
				(float) position.getY() + DISTANCE_TO_BORDERS,
				(float) position.getX() + DISTANCE_TO_BORDERS, (float) height
						- DISTANCE_TO_BORDERS);

		// x-Achse
		window.line((float) position.getX() + DISTANCE_TO_BORDERS,
				(float) position.getY() + height - DISTANCE_TO_BORDERS,
				(float) position.getX() + width - DISTANCE_TO_BORDERS,
				(float) position.getY() + height - DISTANCE_TO_BORDERS);

		DecimalFormat format = new DecimalFormat("#0.#");

		// x-Achse Beschriftung
		float j = minX;
		for (float i = (float) (position.getX() + DISTANCE_TO_BORDERS + stepX
				* labelStepX); i < position.getX() + width
				- DISTANCE_TO_BORDERS; i += stepX * labelStepX) {
			j += labelStepX;
			window.line(i, (float) position.getY() + height
					- DISTANCE_TO_BORDERS, i, (float) position.getY() + height
					- DISTANCE_OF_STROKES_TO_BORDERS);

			window.text(format.format(j),
					i - window.textWidth(format.format(j)) / 2,
					(float) position.getY() + height - 5);
		}

		// y-Achse Beschriftung
		j = minY;
		for (float i = (float) (position.getY() + height - DISTANCE_TO_BORDERS - stepY
				* labelStepY); i > position.getY() + DISTANCE_TO_BORDERS; i -= stepY
				* labelStepY) {

			j += labelStepY;
			window.line((float) position.getX()
					+ DISTANCE_OF_STROKES_TO_BORDERS, i,
					(float) position.getX() + DISTANCE_TO_BORDERS, i);

			window.text(format.format(j), (float) (position.getX() + 1), i);

		}
	}

	private void drawLine(Line line, Integer[] color) {

		window.fill(color[0], color[1], color[2]);
		window.stroke(color[0], color[1], color[2]);

		Point2D start = drawingPointForPoint(new Point2D.Double(minX,
				line.yForX((double) minX)));

		Point2D end = drawingPointForPoint(new Point2D.Double(maxX,
				line.yForX((double) maxX)));

		window.line((float) start.getX(), (float) start.getY(),
				(float) end.getX(), (float) end.getY());
	}

	private void drawPoints(List<Point2D> points, Integer[] color) {
		for (Point2D point : points) {

			Point2D drawingPoint = drawingPointForPoint(point);

			window.fill(color[0], color[1], color[2]);
			window.stroke(color[0], color[1], color[2]);

			window.rect((float) drawingPoint.getX() - 2,
					(float) drawingPoint.getY() - 2, 4, 4);

			window.fill(0);
			DecimalFormat format = new DecimalFormat("###0.##");
			window.text(
					"(" + format.format(point.getX()) + " / "
							+ format.format(point.getY()) + ")",
					(float) drawingPoint.getX() + 5,
					(float) drawingPoint.getY());
		}

	}

	private Point2D drawingPointForPoint(Point2D point) {
		double x = point.getX();
		double y = point.getY();

		return new Point2D.Float(computeX(x), computeY(y));
	}

	private Float computeX(double x) {
		return (float) (x * stepX + DISTANCE_TO_BORDERS - minX * stepX);
	}

	private Float computeY(double y) {
		return (float) (height - DISTANCE_TO_BORDERS - y * stepY + minY * stepY);

	}

}
